---
title: 协同编辑
icon: '#pro/CloudCheck'
---

<MetaData
  lang="zh-CN"
  isPro
  meta={{
    preset: [{
      client: '@univerjs/preset-docs-collaboration',
      locale: '@univerjs/preset-docs-collaboration/locales/zh-CN',
      style: '@univerjs/preset-docs-collaboration/lib/index.css',
    }],
    plugins: [{
      client: '@univerjs-pro/collaboration',
    }, {
      client: '@univerjs-pro/collaboration-client',
      facade: '@univerjs-pro/collaboration-client/facade',
      locale: '@univerjs-pro/collaboration-client/locale/zh-CN',
    }, {
      client: '@univerjs-pro/collaboration-client-ui',
      facade: '@univerjs-pro/collaboration-client-ui/facade',
      style: '@univerjs-pro/collaboration-client-ui/lib/index.css',
    }],
    server: '是',
  }}
/>

<Callout type="warning" title="注意事项">
  协同编辑功能需要 Univer 服务端支持，请确保你已经正确安装并配置了 Univer 服务端。具体请参考：[升级 Pro](/guides/pro)
</Callout>

协同编辑功能允许多个用户同时编辑同一文档，实时同步更改，适用于团队协作和多人编辑场景。

## 预设模式

### 安装

<Callout>
  @univerjs/preset-docs-collaboration 的 `UniverDocsCollaborationPreset` 预设在运行时依赖 `UniverDocsDrawingPreset` 和 `UniverDocsAdvancedPreset` 预设，请先安装 @univerjs/preset-docs-drawing 和 @univerjs/preset-docs-advanced。
</Callout>

```package-install
npm install @univerjs/preset-docs-drawing @univerjs/preset-docs-advanced @univerjs/preset-docs-collaboration
```

### 使用

```typescript
import { UniverDocsAdvancedPreset } from '@univerjs/preset-docs-advanced' // [!code ++]
import UniverPresetDocsAdvancedZhCN from '@univerjs/preset-docs-advanced/locales/zh-CN' // [!code ++]
import { UniverDocsCollaborationPreset } from '@univerjs/preset-docs-collaboration' // [!code ++]
import UniverPresetDocsCollaborationZhCN from '@univerjs/preset-docs-collaboration/locales/zh-CN' // [!code ++]
import { UniverDocsCorePreset } from '@univerjs/preset-docs-core'
import UniverPresetDocsCoreZhCN from '@univerjs/preset-docs-core/locales/zh-CN'
import { UniverDocsDrawingPreset } from '@univerjs/preset-docs-drawing' // [!code ++]
import UniverPresetDocsDrawingZhCN from '@univerjs/preset-docs-drawing/locales/zh-CN' // [!code ++]
import { createUniver, LocaleType, mergeLocales } from '@univerjs/presets'

import '@univerjs/presets/lib/styles/preset-docs-core.css'
import '@univerjs/preset-docs-drawing/lib/index.css' // [!code ++]
import '@univerjs/preset-docs-advanced/lib/index.css' // [!code ++]
import '@univerjs/preset-docs-collaboration/lib/index.css' // [!code ++]

const { univerAPI } = createUniver({
  locale: LocaleType.ZH_CN,
  locales: {
    [LocaleType.ZH_CN]: mergeLocales(
      UniverPresetDocsCoreZhCN,
      UniverPresetDocsDrawingZhCN, // [!code ++]
      UniverPresetDocsAdvancedZhCN, // [!code ++]
      UniverPresetDocsCollaborationZhCN, // [!code ++]
    ),
  },
  collaboration: true, // [!code ++]
  presets: [
    UniverDocsCorePreset(),
    UniverDocsDrawingPreset({ // [!code ++]
      collaboration: true, // [!code ++]
    }), // [!code ++]
    UniverDocsAdvancedPreset({ // [!code ++]
      collaboration: true, // [!code ++]
    }), // [!code ++]
    UniverDocsCollaborationPreset({ // [!code ++]
      universerEndpoint: 'http://localhost:3010', // [!code ++]
    }), // [!code ++]
  ],
})
```

如果你拥有 Univer 的商业许可证，请参考[在客户端使用许可证](/guides/pro/license#在预设模式中使用)进行配置。

{/* ### 预设与配置 */}

## 插件模式

### 安装

```package-install
npm install @univerjs-pro/collaboration @univerjs-pro/collaboration-client @univerjs-pro/collaboration-client-ui
```

### 使用

```typescript
import { UniverCollaborationPlugin } from '@univerjs-pro/collaboration' // [!code ++]
import { UniverCollaborationClientPlugin } from '@univerjs-pro/collaboration-client' // [!code ++]
import { BrowserCollaborationSocketService, UniverCollaborationClientUIPlugin } from '@univerjs-pro/collaboration-client-ui' // [!code ++]
import CollaborationClientZhCN from '@univerjs-pro/collaboration-client/locale/zh-CN' // [!code ++]
import { LocaleType, mergeLocales, Univer } from '@univerjs/core'

import '@univerjs-pro/collaboration-client/facade' // [!code ++]
import '@univerjs-pro/collaboration-client-ui/facade' // [!code ++]

import '@univerjs-pro/collaboration-client/lib/index.css' // [!code ++]

const univer = new Univer({
  locale: LocaleType.ZH_CN,
  locales: {
    [LocaleType.ZH_CN]: mergeLocales(
      CollaborationClientZhCN, // [!code ++]
    ),
  },
})

// 通过将 override 选项设置为 [[IAuthzIoService, null]]，可以告诉 Univer 不要注册内置的 IAuthzIoService。
// 通过将 override 选项设置为 [[IUndoRedoService, null]]，可以告诉 Univer 不要注册内置的 IUndoRedoService
// 这样，Univer 将使用 UniverCollaborationPlugin 中提供的服务作为权限、重做恢复服务的实现。
const univer = new Univer({
  override: [ // [!code ++]
    [IAuthzIoService, null], // [!code ++]
    [IUndoRedoService, null], // [!code ++]
  ], // [!code ++]
})

univer.registerPlugin(UniverCollaborationPlugin) // [!code ++]
univer.registerPlugin(UniverCollaborationClientPlugin, { // [!code ++]
  socketService: BrowserCollaborationSocketService, // [!code ++]
  authzUrl: 'http://localhost:3010/universer-api/authz', // [!code ++]
  snapshotServerUrl: 'http://localhost:3010/universer-api/snapshot', // [!code ++]
  collabSubmitChangesetUrl: 'http://localhost:3010/universer-api/comb', // [!code ++]
  collabWebSocketUrl: 'ws://localhost:3010/universer-api/comb/connect', // [!code ++]
}) // [!code ++]
univer.registerPlugin(UniverCollaborationClientUIPlugin) // [!code ++]
```

如果你拥有 Univer 的商业许可证，请参考[在客户端使用许可证](/guides/pro/license#在插件模式中使用)进行配置。

{/* ### 插件与配置 */}

## 协同文档数据

协同编辑插件依赖 Univer 服务，协同文档的数据存储在 Univer 服务中。

### `unitId`

每篇协同文档在 Univer 服务中都有一个唯一的 `unitId`。Univer 协同客户端使用 `unitId` 可从 Univer 服务获取到对应的协同文档数据进行协同编辑。

### `type`

`type` 是协同文档的类型，协同文档的类型决定了协同文档的初始数据结构。

## 创建协同文档

有多种方式在 Univer 服务中创建协同文档：

- 通过[创建文档](/guides/pro/api#创建文档)接口可以创建一篇空白文档。
- 通过[导入插件](/guides/docs/features/import-export)提供的 `FUniver.importDOCXToUnitIdAsync` 方法，将 `.docx` 文件导入到 Univer 服务。

## 通过 URL 参数加载协同文档

@univerjs-pro/collaboration-client 插件内部提供了根据 URL 参数 `unit` 和 `type` 自动加载对应的数据的功能，这可以简化一些场景下的数据加载逻辑。

如果你想使用该特性，你需要适当地修改一下原有的加载数据逻辑，并将 `unit` 和 `type` 参数添加到 URL 中，如下所示：

```typescript
import { UniverInstanceType } from '@univerjs/core'
// 预设模式下 `UniverInstanceType` 可从 @univerjs/presets 导入
import { UniverInstanceType } from '@univerjs/presets'

// 原有逻辑，只适用于非协同文档
univer.createUnit(UniverInstanceType.UNIVER_DOC, {}) // [!code --]

// 修改后的逻辑，适用于协同文档
const url = new URL(window.location.href) // [!code ++]
const unit = url.searchParams.get('unit') // [!code ++]

if (unit) { // [!code ++]
  // 如果 URL 中包含 unit 参数，则自动加载数据
} else { // [!code ++]
  // 创建一个新的文档
  fetch(`/universer-api/snapshot/${UniverInstanceType.UNIVER_DOC}/unit/-/create`, { // [!code ++]
    method: 'POST', // [!code ++]
    headers: { // [!code ++]
      'Content-Type': 'application/json', // [!code ++]
    }, // [!code ++]
    body: JSON.stringify({ // [!code ++]
      type: UniverInstanceType.UNIVER_DOC, // instance type // [!code ++]
      name: 'New Doc By Univer', // doc name // [!code ++]
      creator: 'user', // creator name // [!code ++]
    }), // [!code ++]
  }).then((response) => { // [!code ++]
    if (!response.ok) { // [!code ++]
      throw new Error('create unit failed') // [!code ++]
    } // [!code ++]
    return response.json() // [!code ++]
  }).then((data) => { // [!code ++]
    if (!data.unitID) { // [!code ++]
      throw new Error('create unit failed') // [!code ++]
    } // [!code ++]
    url.searchParams.set('unit', data.unitID) // [!code ++]
    url.searchParams.set('type', String(UniverInstanceType.UNIVER_DOC)) // [!code ++]
    window.location.href = url.toString() // [!code ++]
  }).catch((error) => { // [!code ++]
    console.error(error) // [!code ++]
  }) // [!code ++]
} // [!code ++]
```

## Facade API

### 加载协同文档

如果你不想使用 URL 参数来加载协同文档，你也可以通过 Facade API 来加载协同文档。

```typescript
const collaboration = univerAPI.getCollaboration()
const workbook = await collaboration.loadSheetAsync('your-unit-id')
```

## 拓展阅读

如果你想进一步了解协同编辑的工作原理，可以阅读以下文章：

- [《OT 算法以及 Univer 的协同编辑设计》](/blog/ot)
